/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package org.atomojo.tools;

import java.io.*;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.restlet.Client;
import org.restlet.Context;
import org.restlet.data.Protocol;
import org.restlet.data.Reference;
import org.restlet.representation.Representation;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;

/**
 *
 * @author alex
 */
public class Restore {
   static final String ATOM_NS = "http://www.w3.org/2005/Atom";
   static final String APP_NS = "http://www.w3.org/2007/app";
   static Logger LOG = Logger.getLogger(Restore.class.getName()); 
   
   public static void usage() {
      System.err.println("Usage: "+Restore.class.getName()+" ( -token server=token ) ( -auth server=(username{:password} | - )) -feed-only service-or-target-dir service");
   }
   
   public static String [] getServerArgument(String value)
   {
      int eq = value.indexOf('=');
      if (eq<0) {
         return null;
      }
      String [] values = new String[2];
      values[0] = value.substring(0,eq);
      values[1] = value.substring(eq+1);
      return values;
   }
   
   public static void main(String[] args) {
      Context context = new Context();
      context.setLogger(LOG);
      
      List<Protocol> clientProtocols = new ArrayList<Protocol>();
      clientProtocols.add(Protocol.HTTP);
      clientProtocols.add(Protocol.HTTPS);
      Client client = new Client(context,clientProtocols);
      
      context.setClientDispatcher(client);
      
      Console console = System.console();
      UserAgent targetBrowser = new UserAgent(context);
      UserAgent sourceBrowser = targetBrowser;
      boolean feedOnly = false;
      int argidx = 0;
      while (argidx<args.length && args[argidx].charAt(0)=='-') {
         if (args[argidx].equals("-token")) {
            argidx++;
            String [] spec = getServerArgument(args[argidx]);
            targetBrowser.setAccessToken(spec[0], spec[1]);
         } else if (args[argidx].equals("-feed-only")) {
             feedOnly = true;
         } else if (args[argidx].equals("-auth")) {
            argidx++;
            String [] spec = getServerArgument(args[argidx]);
            if (spec[1].equals("-")) {
               String username = console.readLine("Username:");
               char [] password = console.readPassword("Password:");
               targetBrowser.setIdentity(spec[0], username, new String(password));
            } else {
               String [] auth = args[argidx].split(":");
               targetBrowser.setIdentity(spec[0], auth[0], auth[1]);
            }
         }
         argidx++;
      }
      
      if ((args.length-argidx)<2) {
         usage();
         System.exit(1);
      }


      Reference sourceRef = new Reference(args[argidx]);
      Reference serviceRef = new Reference(args[argidx+1]);
      File sourceDir = new File(args[argidx]);
      
      if (sourceDir.exists()) {
         if (!sourceDir.isDirectory()) {
            LOG.severe("Not a directory: "+sourceDir.getAbsolutePath());
            System.exit(1);
         }
         
         sourceRef = new Reference((new File(sourceDir,"service.xml")).toURI().toString());
         Context sourceContext = new Context();
         Client sourceClient = new Client(sourceContext,Protocol.FILE);
         sourceContext.setLogger(LOG);

         sourceContext.setClientDispatcher(sourceClient);
         sourceBrowser = new UserAgent(sourceContext);
      }
      
      Restore restore = new Restore(sourceBrowser,targetBrowser);
      restore.setFeedOnly(feedOnly);
      if (!restore.restoreService(sourceRef, serviceRef)) {
         LOG.severe("Restore failed.");
         System.exit(1);
      }
      LOG.info("Restore complete.");
      System.exit(0);
   }
   
   boolean feedOnly;
   Logger logger;
   DocumentWorker sourceWorker;
   DocumentWorker targetWorker;
   
   public Restore(UserAgent sourceAgent,UserAgent targetAgent) 
   {
      logger = LOG;
      feedOnly = false;
      sourceWorker = new DocumentWorker(logger,sourceAgent);
      targetWorker = new DocumentWorker(logger,targetAgent);
   }
   public Logger getLogger() {
      return this.logger;
   }
   
   public void setLogger(Logger log)
   {
      this.logger = log;
   }
   
   public boolean isFeedOnly() {
      return feedOnly;
   }
   
   public void setFeedOnly(boolean value) {
      feedOnly = value;
   }
   
   public boolean restoreService(Reference sourceRef,Reference targetRef)
   {
      
      Document targetServiceDoc = targetWorker.getDocument(targetRef);
      if (targetServiceDoc==null) {
         return false;
      }
      
      List<Element> targetWorkspaces = DocumentWorker.getChildrenByName(targetServiceDoc.getDocumentElement(),APP_NS,"workspace");
      // TODO: which one?  Pick the first for now
      Element targetWorkspace = targetWorkspaces.get(0);
      String collectionsHref = targetWorkspace.getAttribute("href");
      if (collectionsHref==null) {
         LOG.severe("Missing 'href' attribute on target workspace, cannot create collections.");
         return false;
      }
      URI collectionsTarget = URI.create(targetWorkspace.getBaseURI()).resolve(collectionsHref);
      LOG.info("Collections target: "+collectionsTarget);
      
      Document serviceDoc = sourceWorker.getDocument(sourceRef);
      if (serviceDoc==null) {
         return false;
      }
      
      Set<URI> downloadedCollections = new TreeSet<URI>();
      List<Element> workspaces = DocumentWorker.getChildrenByName(serviceDoc.getDocumentElement(),APP_NS,"workspace");
      for (Element workspace : workspaces) {
         List<Element> collections = DocumentWorker.getChildrenByName(workspace,APP_NS,"collection");
         for (Element collection : collections) {
            String href = collection.getAttribute("href");
            URI base = URI.create(collection.getBaseURI());
            URI location = base.resolve(href);
            if (downloadedCollections.contains(location)) {
               // already restored
               continue;
            }
            if (href.indexOf("__index__.atom")>0) {
               href = href.substring(0,href.length()-"__index__.atom".length());
            }
            URI targetCollectionRef = collectionsTarget.resolve(href);
            LOG.info("Processing: "+location+ " -> "+targetCollectionRef);
            Document feedDoc = sourceWorker.getDocument(new Reference(location.toString()));
            if (feedDoc==null) {
               return false;
            }
            
            // Create collection document
            
            Document collectionSpec = DocumentWorker.docBuilder.newDocument();
            Element collectionSpecElement = collectionSpec.createElementNS("http://www.w3.org/2007/app", "collection");
            collectionSpec.appendChild(collectionSpecElement);
            Node current = feedDoc.getDocumentElement().getFirstChild();
            while (current!=null) {
               if (current.getNodeType()!=Node.ELEMENT_NODE) {
                  current = current.getNextSibling();
                  continue;
               }
               Element child = (Element)current;
               String name = child.getLocalName();
               if (name.equals("entry")) {
                  current = null;
                  continue;
               }
               if (name.equals("title") || name.equals("category")) {
                  collectionSpecElement.appendChild(collectionSpec.importNode(child, true));
               }
            }
            
            // Post to workspace URI
            if (!targetWorker.postCreate(new Reference(collectionsTarget), collectionSpec)) {
               LOG.severe("Cannot create feed.");
               return false;
            }
            
            List<Element> entries = DocumentWorker.getChildrenByName(feedDoc.getDocumentElement(),ATOM_NS,"entry");
            for (Element entry : entries) {
               
            }
            
            
            // process all the entries;
            /*
            File feedDir = new File(targetDir,relRef.toString());
            if (!feedDir.exists()) {
               if (!feedDir.mkdirs()) {
                  getLogger().severe("Cannot make feed directory: "+feedDir.getAbsolutePath());
                  return false;
               }
            }
            File feedFile = new File(feedDir,"__index__.atom");
            try {
               Writer output = new OutputStreamWriter(new FileOutputStream(feedFile),"UTF-8");
               writeDocument(feedDoc,output);
               output.close();
            } catch (IOException ex) {
               getLogger().log(Level.SEVERE,"Cannot write feed document "+feedFile.getAbsolutePath()+" due to I/O exception.",ex);
               return false;
            }
            if (!backupMedia(feedDir,feedDoc)) {
               return false;
            }
            */
            downloadedCollections.add(location);
         }
      }
      return true;
   }
   
   public boolean backupMedia(File targetDir,Document feedDoc)
   {
      List<Element> entries = DocumentWorker.getChildrenByName(feedDoc.getDocumentElement(), ATOM_NS, "entry");
      byte [] buffer = new byte[4096];
      for (Element entry : entries) {
         Element content = DocumentWorker.findFirst(entry,ATOM_NS,"content");
         if (content!=null && content.hasAttribute("src")) {
            String src = content.getAttribute("src");
            if (src!=null) {
               URI base = URI.create(content.getBaseURI());
               Reference mediaRef = new Reference(base.resolve(src).toString());
               getLogger().info("Downloading: "+mediaRef);
               Representation entity = sourceWorker.getMedia(mediaRef);
               if (entity==null) {
                  return false;
               }
               InputStream in = null;
               OutputStream out = null;
               try {
                  in = entity.getStream();
                  out = new FileOutputStream(new File(targetDir,src));
                  int len;
                  while ((len=in.read(buffer))>0) {
                     out.write(buffer,0,len);
                  }
               } catch (IOException ex) {
                  getLogger().log(Level.SEVERE,"Cannot download media due to I/O error.",ex);
                  return false;
               } finally {
                  if (in!=null) {
                     try {
                        in.close();
                     } catch (IOException ioex) {}
                  }
                  if (out!=null) {
                     try {
                        out.close();
                     } catch (IOException ioex) {}
                  }
                  entity.release();
               }
            }
         }
      }
      return true;
   }
   
   
}
